using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace MomentumV2
{
    /// <summary>
    /// The screen manager is a component which manages one or more GameScreen
    /// instances. It maintains a stack of screens, calls their Update and Draw
    /// methods at the appropriate times, and automatically routes input to the
    /// topmost active screen.
    /// </summary>
    public class mScreenManager : DrawableGameComponent
    {

        #region Instance Variables

        List<mGameScreen> m_GameScreens = new List<mGameScreen>();
        List<mGameScreen> m_GameScreensToUpdate = new List<mGameScreen>();

        mInputState m_InputState = new mInputState();

        Texture2D m_BlankTexture;
        SpriteBatch m_SpriteBatch;
        SpriteFont m_SpriteFont;

        bool m_IsInitialized;
        bool m_TraceEnabled = false;

        #endregion

        #region Properties

        public mInputState InputState
        {
            get { return m_InputState; }
        }

        public SpriteBatch SpriteBatch
        {
            get { return m_SpriteBatch; }
        }

        public SpriteFont SpriteFont
        {
            get { return m_SpriteFont; }
        }

        public bool TraceEnabled
        {
            get { return m_TraceEnabled; }
        }

        #endregion

        #region Initialization

        public mScreenManager(MomentumV2 momentumV2)
            : base(momentumV2)
        {
        }

        /// <summary>
        /// Initializes the screen manager component.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
            m_IsInitialized = true;
        }

        protected override void LoadContent()
        {
            ContentManager content = Game.Content;

            m_BlankTexture = content.Load<Texture2D>("Interface/Blank");
            m_SpriteBatch = new SpriteBatch(GraphicsDevice);
            m_SpriteFont = content.Load<SpriteFont>("Interface/MenuFont");

            foreach (mGameScreen gameScreen in m_GameScreens)
            {
                gameScreen.LoadContent();
            }
        }

        protected override void UnloadContent()
        {
            foreach (mGameScreen gameScreen in m_GameScreens)
            {
                gameScreen.UnloadContent();
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// Allows each screen to run logic.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            m_InputState.Update();

            m_GameScreensToUpdate.Clear();

            foreach (mGameScreen gameScreen in m_GameScreens)
            {
                m_GameScreensToUpdate.Add(gameScreen);
            }

            bool otherScreenHasFocus = !Game.IsActive;
            bool coveredByOtherScreen = false;

            while (m_GameScreensToUpdate.Count > 0)
            {
                mGameScreen gameScreen = m_GameScreensToUpdate[m_GameScreensToUpdate.Count - 1];

                m_GameScreensToUpdate.RemoveAt(m_GameScreensToUpdate.Count - 1);

                gameScreen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (gameScreen.ScreenState == ScreenState.TransitionOn ||
                    gameScreen.ScreenState == ScreenState.Active)
                {
                    if (!otherScreenHasFocus)
                    {
                        gameScreen.HandleInput(m_InputState);
                        otherScreenHasFocus = true;
                    }

                    if (!gameScreen.IsPopup)
                    {
                        coveredByOtherScreen = true;
                    }
                }
            }

            if (m_TraceEnabled)
            {
                TraceScreens();
            }
        }

        /// <summary>
        /// Prints a list of all the screens, for debugging.
        /// </summary>
        protected void TraceScreens()
        {
            List<string> screenNames = new List<string>();
            foreach (mGameScreen gmaeScreen in m_GameScreens)
            {
                screenNames.Add(gmaeScreen.GetType().Name);
            }
            Trace.WriteLine(string.Join(", ", screenNames.ToArray()));
        }

        #endregion

        #region Draw

        /// <summary>
        /// Tells each screen to draw itself.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            foreach (mGameScreen gameScreen in m_GameScreens)
            {
                if (gameScreen.ScreenState == ScreenState.Hidden)
                {
                    continue;
                }
                gameScreen.Draw(gameTime);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Adds a new screen to the screen manager.
        /// </summary>
        public void AddScreen(mGameScreen gameScreen)
        {
            gameScreen.ScreenManager = this;
            gameScreen.IsExiting = false;

            if (m_IsInitialized)
            {
                gameScreen.LoadContent();
            }

            m_GameScreens.Add(gameScreen);
        }

        /// <summary>
        /// Removes a screen from the screen manager. You should normally
        /// use GameScreen.ExitScreen instead of calling this directly, so
        /// the screen can gradually transition off rather than just being
        /// instantly removed.
        /// </summary>
        public void RemoveScreen(mGameScreen gameScreen)
        {
            if (m_IsInitialized)
            {
                gameScreen.UnloadContent();
            }
            m_GameScreens.Remove(gameScreen);
            m_GameScreensToUpdate.Remove(gameScreen);
        }

        /// <summary>
        /// Expose an array holding all the screens. We return a copy rather
        /// than the real master list, because screens should only ever be added
        /// or removed using the AddScreen and RemoveScreen methods.
        /// </summary>
        public mGameScreen[] GetScreens()
        {
            return m_GameScreens.ToArray();
        }

        /// <summary>
        /// Helper draws a translucent black fullscreen sprite, used for fading
        /// screens in and out, and for darkening the background behind popups.
        /// </summary>
        public void FadeBackBufferToBlack(int alpha)
        {
            Viewport viewport = GraphicsDevice.Viewport;

            SpriteBatch.Begin();

            SpriteBatch.Draw(m_BlankTexture,
                             new Rectangle(0, 0, viewport.Width, viewport.Height),
                             new Color(0, 0, 0, (byte)alpha));

            SpriteBatch.End();
        }

        #endregion

    }
}
